#include <stdio.h>
#include <malloc.h>
#include <pthread.h>
#include <semaphore.h>
#include <math.h>
#include <time.h>
#include <sys/syscall.h>

#define N 10 //constant representing # of students
#define M 15 //constant representing # of books
#define T 5  //constant for the amount of time
#define TRUE 1
#define FALSE 0

/* GLOBAL VARIABLES */
typedef int boolean;

typedef struct {
        int num_pages;
        boolean reading;
        boolean returned;
        struct booktype *previous;
        struct booktype *next;
} booktype;

sem_t sem_people, sem_books;
int total_pages;

/* FUNCTIONS USED */
void *student_thread(void *param);
void meet_at_the_library();
booktype* pick_a_book();
void read_book(booktype* book);
boolean share_impressions(booktype* book);
void return_book(booktype* book);


int main()
{
        int i; //used for counter when creating threads
        int j; //usef for counter when joining threads
       
        //initializing total pages read and semaphores
        total_pages = 0;
        sem_init(&sem_people,0,N);
        sem_init(&sem_books,0,M);
		
		/***********************
		*Values for booktype need to
		*be initialized, should we do
		*it here? (Includes random times)
		************************/

        //creating N student threads
        pthread_t tid[N];
        pthread_attr_t pt_attribs;

        for (i=0; i<N; i++)
        {
                pthread_attr_init(&pt_attribs);
                pthread_create(&(tid[i]), &pt_attribs, student_thread, NULL);
        }

        if (T == 1) sleep(30);

        for (j=0; j<N; j++)
                pthread_join(tid[j], NULL);

        return 0;
}

/************************
*This student_thread function
*is a bit different from his,
*Is that OK?
*************************/
//function for threads provided by Dr. Varsamopoulos
void *student_thread(void *param)
{
        booktype* book = (booktype*) malloc(sizeof(booktype));
        boolean done = FALSE;
        meet_at_the_library();
        do {
                book = pick_a_book();
                read_book(book);
                done = share_impressions(book);
                return_book(book);
        }while(!done);
        free(book);
        pthread_exit(0);
}

void meet_at_the_library()
/*************************
*I believe this one is done
**************************/
{
        //declaration of variables used
        time_t time1,time2;
        struct tm * timeinfo;
        char buffer[20];
        double dif;
        int value;

        //prints out current time & function entered
        time(&time1);
        timeinfo = localtime(&time1);

        strftime(buffer,20,"%H:%M:%S ",timeinfo);
        printf("%s Thread %ld : Entering meet_at_the_library()\n",buffer,(long int) syscall(SYS_gettid));
       
        //uses a semaphore to wait for all of the people to arrive
        sem_wait(&sem_people);
        sem_getvalue(&sem_people, &value);
        while (value > 0)
        {
                sleep(30);
        }
        sem_post(&sem_people);

        //prints out time & function exiting
        time(&time2);
        timeinfo = localtime(&time2);

        strftime(buffer,20,"%H:%M:%S ",timeinfo);
        printf("%s Thread %ld : Exiting meet_at_the_library() ",buffer,(long int) syscall(SYS_gettid));

        //prints out the time difference
        dif = difftime(time2,time1);
        printf("Time taken: %f\n",dif);

}

booktype* pick_a_book()
/**************************
*For each book "picked", If reading
*or returned is set to true, book
*cannot be chosen.  If book is chosen
*to set reading to true.   
****************************/
{
        //declaration of variables used
        booktype *book = (booktype *) malloc(sizeof(booktype));
        time_t time1,time2;
        struct tm * timeinfo;
        char buffer[20];
        double dif;

        //prints out current time & function entered
        time(&time1);
        timeinfo = localtime(&time1);

        strftime(buffer,20,"%H:%M:%S ",timeinfo);
        printf("%s Thread %ld : Entering pick_a_book()\n",buffer,(long int) syscall(SYS_gettid));
       
        //prints out time & function exiting
        time(&time2);
        timeinfo = localtime(&time2);

        strftime(buffer,20,"%H:%M:%S ",timeinfo);
        printf("%s Thread %ld : Exiting pick_a_book() ",buffer,(long int) syscall(SYS_gettid));

        //prints out the time difference
        dif = difftime(time2,time1);
        printf("Time taken: %f\n",dif);

        return book;
}

void read_book(booktype* book)
/******************************
*This fucntion returns when T x P[bi]
*microseconds have passed (mean of 
*exponential distribution of reading time
*of book)
*******************************/
{
        //declaration of variables used
        time_t time1,time2;
        struct tm * timeinfo;
        char buffer[20];
        double dif;

        //prints out current time & function entered
        time(&time1);
        timeinfo = localtime(&time1);

        strftime(buffer,20,"%H:%M:%S ",timeinfo);
        printf("%s Thread %ld : Entering read_book()\n",buffer,(long int) syscall(SYS_gettid));
       
        //prints out time & function exiting
        time(&time2);
        timeinfo = localtime(&time2);

        strftime(buffer,20,"%H:%M:%S ",timeinfo);
        printf("%s Thread %ld : Exiting read_book() ",buffer,(long int) syscall(SYS_gettid));

        //prints out the time difference
        dif = difftime(time2,time1);
        printf("Time taken: %f\n",dif);

}

boolean share_impressions(booktype* book)
/*******************************
*Returns when average time proportional
*to number of pages read for shared session
*has passed.  Always returns done=false
*unless there are no more books left to be
*read
********************************/
{
        //declaration of variables used
        time_t time1,time2;
        struct tm * timeinfo;
        char buffer[20];
        double dif;
        int value;

        //prints out current time & function entered
        time(&time1);
        timeinfo = localtime(&time1);

        strftime(buffer,20,"%H:%M:%S ",timeinfo);
        printf("%s Thread %ld : Entering share_impressions()\n",buffer,(long int) syscall(SYS_gettid));

        //waits for all of the people to arrive and totals amount of pages read
        //still needs to calculate the time exponentially      
        sem_wait(&sem_people);
        total_pages += book->num_pages;
        sem_getvalue(&sem_people, &value);
        while (value > 0)
        {
                sleep(30);
        }
        sem_post(&sem_people);

        //prints out time & function exiting
        time(&time2);
        timeinfo = localtime(&time2);

        strftime(buffer,20,"%H:%M:%S ",timeinfo);
        printf("%s Thread %ld : Exiting share_impressions() ",(long int) syscall(SYS_gettid));

        //prints out the time difference
        dif = difftime(time2,time1);
        printf("Time taken: %f\n",dif);
        return TRUE;
}

void return_book(booktype* book)
/******************************
*Set returned of each book to true
******************************/
{
        //declaration of variables used
        time_t time1,time2;
        struct tm * timeinfo;
        char buffer[20];
        double dif;

        //prints out current time & function entered
        time(&time1);
        timeinfo = localtime(&time1);

        strftime(buffer,20,"%H:%M:%S ",timeinfo);
        printf("%s Thread %ld : Entering return_book()\n",buffer,(long int) syscall(SYS_gettid));
       
        //prints out time & function exiting
        time(&time2);
        timeinfo = localtime(&time2);

        strftime(buffer,20,"%H:%M:%S ",timeinfo);
        printf("%s Thread %ld : Exiting return_book() ",buffer,(long int) syscall(SYS_gettid));

        //prints out the time difference
        dif = difftime(time2,time1);
        printf("Time taken: %f\n",dif);

}
